package oh.how.easy.fck.components;

import java.util.Locale;
import oh.how.easy.fck.base.AbstractEditorTextField;
import oh.how.easy.fck.constants.FckEditorConstants;
import oh.how.easy.fck.services.FckEditorLocaleService;
import oh.how.easy.fck.services.FckEditorService;
import oh.how.easy.fck.util.LegacyT5ServiceHelper;
import org.apache.tapestry5.Asset;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.RenderSupport;
import org.apache.tapestry5.annotations.AfterRender;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.IncludeJavaScriptLibrary;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.ioc.annotations.Symbol;
import org.apache.tapestry5.services.ApplicationGlobals;

/**
 * Adapted from ChennilleKit
 * @author Ville
 */
/**
 * Parts of this component are borrowed from <a href="http://www.chenillekit.org">ChennilleKit</a>. <br />
 * <p>The editor component provides a rich text editor as a form control.
 * Based on the <a href="http://www.fckeditor.net/">FCKeditor</a>, the editor
 * is highly configurable (and can therefore be complicated). This component
 * aims to keep usage simple, outsourcing most of the configuration to an
 * optional external javascript file.</p>
 * <p/>
 * <p>The most important configurations are that of an external configuration
 * file and the toolbars present in the editor. To support this, the editor component
 * exposes the <code>configuration</code> and <code>toolbarSet</code>
 * parameters.</p>
 * <p/>
 * <p>In the interest of usability, the editor component will function as
 * classic textarea element.</p>
 * <p>
 * The language of this editor defaults to the current T5 locale. If you want to
 * change this behaviour just implement interface {@link FckEditorLocaleService} and bind
 * the service implementation to that interface in your app module. These settings are applied to every editor instance.
 * If you whish to change the language of just one editor, use the customLocale parameter.
 * (If you need more than one language in one page for an example.)</p>
 * <p>
 * The order in which the locales are honored is: <br/>
 * 1. customLocale <br/>
 * 2. FckEditorLocaleService implementation <br/>
 * 3. Tapestry 5 locale (@Inject private Locale locale) <br/>
 * </p>
 * <p>
 * To use file upload capabilities you must implement {@link FckEditorPathService} and
 * {@link FckEditorUserRightService} interfaces. Remember to bind the service implementations
 * to corresponding interfaces in your app module. 
 * You must also expose the save location(s) to www, so additional web server
 * configuration might be required depending on your configuration.<br /> If you need more exotic file connector
 * you can implement {@link FckEditorConnector}. (To save files to database etc.)
 * The default connector is the one distributed with java.fckeditor.net, {@link LocalConnector}.
 * <br />
 * </p>
 * <p/>
 * <p>NOTE: This component is built on the 2.x version of FCKeditor. To use the newer version please use T5 Easy Ck Editor.</p>
 *
 * @author Ville Virtanen
 * @see <a href="http://docs.fckeditor.net/FCKeditor_2.x/Developers_Guide">FCKeditor developer's guide</a>
 * @see <a href="http://docs.fckeditor.net/FCKeditor_2.x/Users_Guide">FCKeditor user's guide</a>
 */
@IncludeJavaScriptLibrary("classpath:oh/how/easy/fck/js/fckeditor/fckeditor.js")
public class FckEditor extends AbstractEditorTextField {
    
    /**
     * The height of the editor.
     */
    @Parameter(defaultPrefix = "literal", value = "300px")
    private String _height;

    /**
     * The width of the editor.
     */
    @Parameter(defaultPrefix = "literal", value = "600px")
    private String _width;

    /**
     * A custom configuration for this editor.
     * See the FCKeditor manual for details on custom configurations.
     * DO NOT configure ANY CONNECTOR related stuff.
     */
    @Parameter(allowNull=false)
    private Asset _configuration;

    /**
     * The path to the skin (FCKConfig.SkinPath). Use only the path after context. Ie. use /assets/js/skin/, easy fck editor automatically
     * adds the context to it. This is only used if the configuration parameter is used also.
     */
    @Parameter(defaultPrefix = "literal")
    private String _skinPath;

    /**
     * A custom locale to be used, if for some reason the page contains multilocale editors.
     */
    @Parameter(allowNull=false)
    private Locale customLocale;

    /**
     * The toolbar set to be used with this editor. Default possible values
     * are <code>Default</code> and <code>Basic</code>.
     * Toolbar sets can be configured in a {@link #_configuration custom configuration}.
     */
    @Parameter(defaultPrefix = "literal", value = "Default")
    private String _toolbarSet;

    @Inject
    @Symbol(FckEditorConstants.EDITOR_CONTEXT)
    private String location;

    @Environmental
    private RenderSupport _pageRenderSupport;

    @Inject
    private FckEditorService editorService;

    @Inject @Symbol(FckEditorConstants.APPLICATION_CONTEXT)
    private String context;
    
    private String _value;

    @Inject
    private Locale locale;

    @Inject
    private ApplicationGlobals applicationGlobals;

    @Inject
    private ComponentResources componentResources;

    @Override
    protected final void writeFieldTag(MarkupWriter writer, String value)
    {
        // At it's most basic level, editor should function as a textarea.
        writer.element("textarea",
                       "name", getControlName(),
                       "id", getClientId(),
                       "cols", getWidth());

        // Save until needed in afterRender().
        _value = value;
    }

    @AfterRender
    final void afterRender(MarkupWriter writer)
    {
        if (_value != null) writer.write(_value);
        writer.end();
        writeScript();
    }

    final void writeScript()
    {
        String editorVar = "easy_fck_editor_" + getClientId().replace(':', '_');

        _pageRenderSupport.addScript("var %s = new FCKeditor(\"%s\");", editorVar, getClientId());

        _pageRenderSupport.addScript("%s.BasePath = '%s';", editorVar, context + "assets/" + location + "/fckeditor/");

        if(_configuration != null) {
            if(_skinPath != null) {
                editorService.persistEditorConfiguration(_configuration, _skinPath);
            } else {
                editorService.persistEditorConfiguration(_configuration);
            }
            _pageRenderSupport.addScript("%s.Config['CustomConfigurationsPath'] = '%s';", editorVar, context + "assets/" + location + "/configure?id=" + _configuration.getResource().getPath());
        }

        if(_toolbarSet != null) {
            _pageRenderSupport.addScript("%s.ToolbarSet = '%s';", editorVar, _toolbarSet);
        }

        //Language
        _pageRenderSupport.addScript("%s.Config['AutoDetectLanguage'] = false;", editorVar);
        if(componentResources.isBound("customLocale")) {
            _pageRenderSupport.addScript("%s.Config['DefaultLanguage'] = '%s';", editorVar, customLocale.getLanguage());
        } else {
            FckEditorLocaleService localeService = LegacyT5ServiceHelper.getService(FckEditorLocaleService.class, applicationGlobals.getServletContext());
            if(localeService != null && localeService.getLocale() != null) {
                _pageRenderSupport.addScript("%s.Config['DefaultLanguage'] = '%s';", editorVar, localeService.getLocale().getLanguage());
            } else {
                _pageRenderSupport.addScript("%s.Config['DefaultLanguage'] = '%s';", editorVar, locale.getLanguage());
            }
        }

        //Width && Height
        _pageRenderSupport.addScript("%s.Height = '%s';", editorVar, _height);
        _pageRenderSupport.addScript("%s.Width = '%s';", editorVar, _width);
        _pageRenderSupport.addScript("%s.ReplaceTextarea();", editorVar);
    }
    
}